package require tclodbc
package require ::wt::database::clients::tclOdbcFactory
package provide ::wt::session::db::odbc::sqlServerSession 1

# Initialize the SQL Server session driver

namespace eval ::wt::session::db::odbc::sqlServerSession:: {
  variable dataSourceId ""
  variable db ""
  variable sessionTable WtSession
  variable entryTable WtSessionEntry
  variable types
}

# Open the session database

proc ::wt::session::db::odbc::sqlServerSession::openDb {} {
  variable db
  if {$db == ""} {
    variable dataSourceId
    set db [::wt::database::clients::tclOdbcFactory open $dataSourceId]
  }
  return $db
}

# Create a new session

proc ::wt::session::db::odbc::sqlServerSession::create {id props} {
  openDb
  variable db

  # Insert the session record

  set types {{char 32} {char 15} {char 128}}
  set vals [list $id [table get $props userIp] [table get $props userAgent]]
  db "insert into $sessionTable (sessionId, userIp, userAgent) \
    values (?, ?, ?)" $types $vals

  # Return the id used

  return $id
}

# Fetch all the session entries

proc ::wt::session::db::odbc::sqlServerSession::fetch {id items} {
  openDb
  variable db

  # Fetch entries

  set types {{char 32}}
  set vals [list $id]
  set entries [db "select name, value from $entryTable where sessionId = ?" \
    $types $vals]

  foreach {name value} $entries {
    table add $items $name $value
  }
}

# Write the changes

proc ::wt::session::db::odbc::sqlServerSession::commit {id updates options} {
  openDb
  variable db

  foreach {cleared keysRemoved entriesRemoved entriesAdded accessed} $updates

  # Handle the deletions

  if {$cleared} {
    set types {{CHAR 32}}
    set vals [list $id]
    db "delete from $entryTable where sessionId = ?" $types $vals
  } else {
    set terms [list]
    set types {{CHAR 32}}
    set vals [list $id]
    foreach key $keysRemoved {
      lappend terms {name = ?}
      lappend types {CHAR 256}
      lappend vals $key
    }
    foreach {key val} $entriesRemoved {
      lappend terms {(name = ? and value = ?)}
      lappend types {CHAR 256}
      lappend types {VARBINARY 8000}
      lappend vals $key
      lappend vals $val
    }
    if {[llength $terms]} {
      set sql "delete from $entryTable where sessionId = ? and ("
      append sql [join $terms " or "]
      append sql ")"
      db $sql $types $vals
    }
  }

  # Handle the insertions

  if {[llength $entriesAdded]} {
    set types [list]
    set vals [list]
    set valsSql ""
    foreach {key val} $entriesAdded {
      lappend types {CHAR 256}
      lappend types {VARBINARY 8000}
      lappend vals $key
      lappend vals $val
      if {$valsSql != ""} {
        append valsSql ", "
      }
      append valsSql "(?, ?)"
    }
    set sql "insert into $entryTable (name, value) values $valsSql"
    db $sql $types $vals
  }

  # Update the access time

  if {$accessed} {
    set types {{CHAR 32}}
    set vals [list $id]
    db "update $sessionTable set accessTime = NOW() where sessionId = ?" \
      $types $vals
  }
}

# Remove a session

proc ::wt::session::db::odbc::sqlServerSession::destroy {id} {
  openDb
  variable db

  set types {{CHAR 32}}
  set vals [list $id]
  db "delete from $sessionTable where sessionId = ?" $types $vals
}

# Cleanup stale sessions

proc ::wt::session::db::odbc::sqlServerSession::cleanup {maxIdleTime} {
  openDb
  variable db

  set types {INTEGER}
  set vals [list $maxIdleTime]
  set n [db "delete from $sessionTable where accessTime < \
    dateadd(second, getdate(), ?) $types $vals"]

  reutrn $n
}
